import asyncio
import time
import random

import pygame

import cfg
from debug import debug
from tetris.blocks import block_s, block_i, block_j, block_l, block_o, block_t, block_z
from units.uiUtil import drawText, gameBgImg


bg = pygame.image.load('assets/image/bg.png')

SCREEN_WIDTH, SCREEN_HEIGHT = 720, 1000
BLOCK_COL_NUM = 10  # 每行的方格数
SIZE = 50  # 每个小方格大小
BLOCK_ROW_NUM = 20  # 每列的方个数
BORDER_WIDTH = 4  # 游戏区边框宽度
stop_all_block_list = [['.' for i in range(BLOCK_COL_NUM)] for j in range(BLOCK_ROW_NUM)]
current_block = []  # 当前图形
current_block_start_row = -2  # 当前图片从哪一行开始显示图形
current_block_start_col = 4  # 当前图形从哪一列开始显示
next_block = [] # 下一个图形

class MainTetris:
    def __init__(self, dims, FPS=60):
        self.dims = dims
        self.FPS = FPS
        # 游戏运行控制变量（gamen_running）
        self.game_running = True
        self.score = 0

    # 创建一个图形
    def get_block(self):
        block_style_list = random.choice([block_s, block_i, block_j, block_l, block_o, block_t, block_z])
        return random.choice(block_style_list)

    # 判断是否碰撞到其它图形或者底边界
    def judge_move_down(self,current_block, current_block_start_row, current_block_start_col, stop_all_block_list):
        # 得到其它图形所有的坐标
        stop_all_block_position = list()
        for row, line in enumerate(stop_all_block_list):
            for col, block in enumerate(line):
                if block != ".":
                    stop_all_block_position.append((row, col))

        # 判断碰撞
        for row, line in enumerate(current_block):
            if 'O' in line and current_block_start_row + row >= BLOCK_ROW_NUM:
                # 如果当前行有0，且从起始行开始算+当前显示的行，超过了总行数，那么就认为碰到了底部
                return False
            for col, block in enumerate(line):
                if block != "." and (current_block_start_row + row, current_block_start_col + col) in stop_all_block_position:
                    return False

        return True

    # 将当前已经停止移动的block添加到列表中
    def add_to_stop_all_block_list(self,stop_all_block_list, current_block, current_block_start_row, current_block_start_col):
        for row, line in enumerate(current_block):
            for col, block in enumerate(line):
                if block != '.':
                    stop_all_block_list[current_block_start_row + row][current_block_start_col + col] = "O"

    def change_speed(self, score):
        speed_level = [("1", 0.5, 0, 20), ("2", 0.4, 21, 50), ("3", 0.3, 51, 100), ("4", 0.2, 101, 200),
                       ("5", 0.1, 201, None)]
        for speed_info, speed, score_start, score_stop in speed_level:
            if score_stop and score_start <= score <= score_stop:
                return speed_info, speed
            elif score_stop is None and score >= score_start:
                return speed_info, speed

    # 判断游戏是否结束
    def judge_game_over(self,stop_all_block_list):
        if "O" in stop_all_block_list[0]:
            return False
        else:
            return True

    # 判断是否可以向左移动
    def judge_move_left(self, current_block, current_block_start_col):
        # 先判断列的方式是从左到右
        for col in range(len(current_block[0])):
            # 得到1列的所有元素
            col_list = [line[col] for line in current_block]
            # 判断是否碰到左边界
            if 'O' in col_list and current_block_start_col + col < 0:
                return False
        return True
    # 判断是否可以向右移动
    def judge_move_right(self, current_block, current_block_start_col):
        # 先判断列的方式是从右到左
        for col in range(len(current_block[0]) - 1, -1, -1):
            # 得到1列的所有元素
            col_list = [line[col] for line in current_block]
            # 判断是否碰到右边界
            if 'O' in col_list and current_block_start_col + col >= BLOCK_COL_NUM:
                return False
        return True
    # 旋转
    def change_current_block_style(self, current_block):
        # 计算出，当前图形样式属于哪个图形
        current_block_style_list = None
        for block_style_list in [block_s, block_i, block_j, block_l, block_o, block_t, block_z]:
            if current_block in block_style_list:
                current_block_style_list = block_style_list
        # 得到当前正在用的图形的索引（下标）
        index = current_block_style_list.index(current_block)
        # 它的下一个图形的索引
        index += 1
        # 防止越界
        index = index % len(current_block_style_list)
        # 返回下一个图形
        return current_block_style_list[index]

    # 消除
    def judge_lines(self, stop_all_block_list):
        # 记录刚刚消除的行数
        move_row_list = list()
        # 消除满格的行
        for row, line in enumerate(stop_all_block_list):
            if "." not in line:
                # 如果这一行没有. 那么就意味着全部是O，则消除这一行
                stop_all_block_list[row] = ['.' for _ in range(len(line))]
                move_row_list.append(row)
        # 如果没有满格的行，则结束此函数
        if not move_row_list:
            return 0
        # 移动剩余的行到下一行
        for row in move_row_list:
            stop_all_block_list.pop(row)
            stop_all_block_list.insert(0, ['.' for _ in range(len(line))])

        return len(move_row_list)

    # 游戏主循环所在函数需要由 async 定义
    async def start(self):
        # 初始化游戏界面（screen）：尺寸、背景色等
        screen = pygame.display.set_mode(self.dims)
        # 显示游戏名称
        pygame.display.set_caption("俄罗斯方块")
        # 初始化游戏时钟（clock），由于控制游戏帧率
        clock = pygame.time.Clock()
        # 播放背景音乐
        # playMyBgMusic('')

        current_block = self.get_block()  # 当前图形
        current_block_start_row = -2  # 当前图片从哪一行开始显示图形
        current_block_start_col = 4  # 当前图形从哪一列开始显示
        next_block = self.get_block()  # 下一个图形
        last_time = time.time()
        speed = 0.5  # 降落的速度
        speed_info = '1'  # 显示的速度等级

        # 定义一个列表，用来存储所有的已经停止移动的形状
        stop_all_block_list = [['.' for i in range(BLOCK_COL_NUM)] for j in range(BLOCK_ROW_NUM)]

        # 游戏主循环
        while cfg.inGame:
            # 按照给定的 FPS 刷新游戏
            # clock.tick() 函数返回上一次调用该函数后经历的时间，单位为毫秒 ms
            # dt 记录上一帧接受之后经历的时间，单位为秒 m
            # 使用 dt 控制物体运动可以使游戏物理过程与帧率无关
            dt = clock.tick(self.FPS) / 1000.0
            # 使用 asyncio 同步
            # 此外游戏主体代码中不需要再考虑 asyncio
            await asyncio.sleep(0)


            # 游戏事件处理
            # 包括键盘、鼠标输入等
            for event in pygame.event.get():
                # 点击关闭窗口按钮或关闭网页
                if event.type == pygame.QUIT:
                    pygame.quit()
                elif event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_LEFT:
                        if self.judge_move_left(current_block, current_block_start_col - 1):
                            current_block_start_col -= 1
                    elif event.key == pygame.K_RIGHT:
                        if self.judge_move_right(current_block, current_block_start_col + 1):
                            current_block_start_col += 1
                    elif event.key == pygame.K_UP:
                        current_block_next_style = self.change_current_block_style(current_block)
                        if self.judge_move_left(current_block_next_style, current_block_start_col) and \
                                self.judge_move_right(current_block_next_style, current_block_start_col) and \
                                self.judge_move_down(current_block, current_block_start_row, current_block_start_col,
                                                stop_all_block_list):
                            # 判断新的样式没有越界
                            current_block = current_block_next_style
                    elif event.key == pygame.K_DOWN:
                        # 判断是否可以向下移动，如果碰到底部或者其它的图形就不能移动了
                        while True:
                            if self.judge_move_down(current_block, current_block_start_row + 1, current_block_start_col,
                                               stop_all_block_list):
                                current_block_start_row += 1
                            else:
                                break

                elif event.type == pygame.MOUSEBUTTONDOWN and event.button:
                    if not self.game_running:
                        # 重置游戏用到的变量
                        current_block = self.get_block()  # 当前图形
                        current_block_start_row = -2  # 当前图片从哪一行开始显示图形
                        current_block_start_col = 4  # 当前图形从哪一列开始显示
                        next_block = self.get_block()  # 下一个图形
                        stop_all_block_list = [['.' for i in range(BLOCK_COL_NUM)] for j in range(BLOCK_ROW_NUM)]
                        self.score = 0
                        self.game_running = True

            # 判断是否修改当前图形显示的起始行
            if self.game_running and time.time() - last_time > speed:
                last_time = time.time()
                # 判断是否可以向下移动，如果碰到底部或者其它的图形就不能移动了
                if self.judge_move_down(current_block, current_block_start_row + 1, current_block_start_col,
                                   stop_all_block_list):
                    current_block_start_row += 1
                else:
                    # 将这个图形存储到统一的列表中，这样便于判断是否成为一行
                    self.add_to_stop_all_block_list(stop_all_block_list, current_block, current_block_start_row,
                                               current_block_start_col)
                    # 判断是否有同一行的，如果有就消除，且加上分数
                    self.score += self.judge_lines(stop_all_block_list)
                    # 判断游戏是否结束（如果第一行中间有O那么就表示游戏结束）
                    self.game_running = self.judge_game_over(stop_all_block_list)
                    # 调整速度
                    speed_info, speed = self.change_speed(self.score)
                    # 创建新的图形
                    current_block = next_block
                    next_block = self.get_block()
                    # 重置数据
                    current_block_start_col = 4
                    current_block_start_row = -2

            # 以背景色覆盖刷新游戏界面
            gameBgImg(screen, bg, 270, self.dims)

            # 调用 debug 函数在游戏界面左上角显示游戏帧率
            debug(f"{clock.get_fps():.1f}", SIZE * BLOCK_COL_NUM + 20, 10, color='green')
            # 显示分数
            drawText(f'得分：{self.score}', "black", (SIZE * BLOCK_COL_NUM + 20, 100), screen)
            # 显示速度
            drawText(f'速度：{speed}', "black", (SIZE * BLOCK_COL_NUM + 20, 150), screen)
            # 下一个
            drawText('下一个', "black", (SIZE * BLOCK_COL_NUM + 20, 200), screen)
            # 按向上箭头↑旋转
            drawText('向上箭头↑', "black", (SIZE * BLOCK_COL_NUM + 20, 700), screen)
            drawText('旋转', "black", (SIZE * BLOCK_COL_NUM + 20, 750), screen)

            # 画游戏区域分隔线
            pygame.draw.line(screen, (100, 40, 200), (SIZE * BLOCK_COL_NUM, 0), (SIZE * BLOCK_COL_NUM, self.dims[1]),
                             BORDER_WIDTH)

            # 显示当前图形
            for row, line in enumerate(current_block):
                for col, block in enumerate(line):
                    if block != '.':
                        pygame.draw.rect(screen, (20, 128, 200), (
                        (current_block_start_col + col) * SIZE, (current_block_start_row + row) * SIZE, SIZE, SIZE), 0)
            # 显示所有停止移动的图形
            for row, line in enumerate(stop_all_block_list):
                for col, block in enumerate(line):
                    if block != '.':
                        pygame.draw.rect(screen, (20, 128, 200), (col * SIZE, row * SIZE, SIZE, SIZE), 0)

            # 画网格线 竖线
            for x in range(BLOCK_COL_NUM):
                pygame.draw.line(screen, (0, 0, 0), (x * SIZE, 0), (x * SIZE, BLOCK_ROW_NUM * SIZE), 1)
            # 画网格线 横线
            for y in range(BLOCK_ROW_NUM):
                pygame.draw.line(screen, (0, 0, 0), (0, y * SIZE), (BLOCK_COL_NUM * SIZE, y * SIZE), 1)
            pygame.draw.line(screen, (0, 0, 0), (0, BLOCK_ROW_NUM * SIZE), (BLOCK_COL_NUM * SIZE, BLOCK_ROW_NUM * SIZE), 1)

            # 下一个图形（图形）
            start_row = 6
            for row, line in enumerate(next_block):
                for col, block in enumerate(line):
                    if block != '.':
                        pygame.draw.rect(screen, (20, 128, 200),
                                         (520 + SIZE * col, (start_row + row) * SIZE, SIZE, SIZE), 0)
                        # 显示这个方格的4个边的颜色
                        # 左
                        pygame.draw.line(screen, (0, 0, 0), (520 + SIZE * col, (start_row + row) * SIZE),
                                         (520 + SIZE * col, (start_row + row + 1) * SIZE), 1)
                        # 上
                        pygame.draw.line(screen, (0, 0, 0), (520 + SIZE * col, (start_row + row) * SIZE),
                                         (520 + SIZE * (col + 1), (start_row + row) * SIZE), 1)
                        # 下
                        pygame.draw.line(screen, (0, 0, 0), (520 + SIZE * col, (start_row + row + 1) * SIZE),
                                         (520 + SIZE * (col + 1), (start_row + row + 1) * SIZE), 1)
                        # 右
                        pygame.draw.line(screen, (0, 0, 0), (520 + SIZE * (col + 1), (start_row + row) * SIZE),
                                         (520 + SIZE * (col + 1), (start_row + row + 1) * SIZE), 1)

            # 显示游戏结束画面
            if not self.game_running:
                    drawText('游戏结束', "black", (SIZE/2 * BLOCK_COL_NUM, 200), screen)
                    drawText('鼠标点击 任意位置继续', "black", (SIZE/2 * BLOCK_COL_NUM, 300), screen)

            # 将游戏界面内容输出至屏幕
            pygame.display.update()

        # 跳出游戏主循环，退出游戏
        cfg.inGame = False